# -*- coding:utf-8 -*-

# 线程启动
import threading
import time

'''
def worker():
    for _ in range(10):
        time.sleep(0.5)
        print('welcome come to linux')

def worker1():
    for _ in range(10):
        time.sleep(0.5)
        print('welcome .....')

t = threading.Thread(target=worker,name='worker') # 线程对象.
t.start()  # 启动
# 通过threading.Thread创建一个线程对象,target是目标函数,name可以指定名称.　但是线程没有启动,需要调用start方法.

t = threading.Thread(target=worker1,name="worker1")
t.start()
'''

# 死循环
'''
def worker3():
    while True:
        time.sleep(1)
        print('yuan worker3')
    print('Finshed')

c = threading.Thread(target=worker3,name="worker3")
c.start()
'''

# 线程退出
'''
def worker():
    for _ in range(10):
        time.sleep(0.5)
        print('welcome threading')
    print('Thread over')

def worker5():
    count = 0
    while True:
        time.sleep(0.5)
        print('welcome worker5')
        count += 1
        if count > 3:
            raise Exception('Exec exception')
            break
    print('Thread over.....')

t = threading.Thread(target=worker,name="worker")
t.start()

t = threading.Thread(target=worker5,name="worker5")
t.start()
'''


# 线程传参
'''
def add(x,y):
    print('{} + {} = {}'.format(x,y,x + y,threading.current_thread().ident))

th1 = threading.Thread(target=add,name='add',args=(4,5)) # 线程对象
th1.start()  # 启动
time.sleep(2)

th2 = threading.Thread(target=add,name='add',args=(5,),kwargs={'y':10})
th2.start()

th3 = threading.Threat(target=add,name='add',kwargs={'x':4,'y':5})
th3.start()
'''

# threading的属性和方法
'''
def worker7(n=5):
    print(threading.current_thread())
    print(threading.main_thread())
    print(threading.active_count())
    for _ in range(10):
        time.sleep(0.5)
        print('welcome')
    print('Thread over')

d = threading.Thread(target=worker7,name='worker7')
d.start()
'''

# threading的属性和方法
'''
def showthreadinfo():
    print("currentthread = {}".format(threading.current_thread()))
    print("main thread = {}".format(threading.main_thread()))
    print("active count = {}".format(threading.active_count()))

def worker8():
    count = 0
    showthreadinfo()
    while True:
        if (count > 5):
            break
        time.sleep(1)
        count += 1
        print("welcome to start learning threading")

f = threading.Thread(target=worker8,name="worker8")
showthreadinfo()
f.start()

'''

'''
def worker9():
    count = 0
    while True:
        if (count > 5):
            break
        time.sleep(1)
        count += 1
        print(threading.current_thread().name)

g = threading.Thread(name='worker9',target=worker9)
print(g.ident)
g.start()

while True:
    time.sleep(1)
    if g.is_alive():
        print('{} {} alive'.format(g.name,g.ident))
    else:
        print('{} {} dead'.format(g.name,g.ident))
        g.start() # 不可以再次运行. 报错:RuntimeError: threads can only be started once
'''
'''
class MyThread(threading.Thread):
    def run(self):
        print('run')
        super().run()
    
    def start(self):
        print('start')
        return super().start()

def worker10(n=5):
    for _ in range(10):
        time.sleep(1)
        print('welcome to threading')
    print('Thread over')


t = MyThread(target=worker10,name='worker10')
t.run()
'''


# 多线程
'''
def worker11():
    count = 0
    while True:
        if (count > 5):
            break
        time.sleep(0.5)
        count += 1
        print('worker11 running')
        print(threading.current_thread().name,threading.current_thread().ident)

def worker12():
    count = 0
    while True:
        if (count > 5):
            break
        time.sleep(0.5)
        count += 1
        print('worker12 running')
        print(threading.current_thread().name,threading.current_thread().ident)


class Mythrea(threading.Thread):
    def start(self):
        print('start ---------')
        super().start()

    def run(self):
        print('run ----------')
        super().run()  # 看看父类在做什么

t2 = Mythrea(name='worker11',target=worker11)
t3 = Mythrea(name='worker12',target=worker12)

# t2.start()
# t3.start()

t2.run()
t3.run()
#   start和run的区别?
#   在线程函数中,增加打印线程的名字的语句,看看能看到什么信息.
'''



# 线程安全
# 在IPython中演示,python命令行,vscode不能演示出效果.
"""
import threading,logging

logging.basicConfig(level=logging.INFO)

def worker():
    for x in range(100):
        # print("{} is running\n".format(threading.current_thread(),end=""))
        msg = "{} is running.".format(threading.current_thread())
        logging.info(msg)


for x in range(1,5):
    name = "worker{}".format(x)
    t = threading.Thread(target=worker,name="worker-{}".format(x)).start()
"""



# daemon线程和non-daemon线程
'''
import time,threading

def foo():
    time.sleep(5)
    for i in range(20):
        print(i)

    
# 主线程是non-daemon线程
t = threading.Thread(target=foo,daemon=False)
t.start()
print('Main Thread Exiting')
'''
"""
import logging,threading
logging.basicConfig(level=logging.INFO)

def worker():
    for x in range(100):
        msg = "{} is running".format(threading.current_thread())
        logging.info(msg)
        # print(threading.enumerate())
        threading.Thread(target=worker1,name="worker1-{}".format(x),daemon=False).start()

def worker1():
    for x in range(10):
        msg = "$$$$ {} is running".format(threading.current_thread())
        logging.info(msg)


# threading.Thread(target=worker,name="worker-{}".format(0)).start()
threading.Thread(target=worker,name="worker={}".format(0),daemon=True).start()

# threading.Thread(target=worker1,name="worker1-{}".format(0)).start()

time.sleep(0.5)
print('ending')
print(threading.enumerate())

"""

"""
import time,threading

def bar():
    time.sleep(10)
    print('bar')

def foo():
    for i in range(20):
        print(i)
    t = threading.Thread(target=bar,daemon=False)
    t.start()

# 主线程是non-daemon线程
t = threading.Thread(target=foo,daemon=True)
t.start()
print('Main Thread Exiting')
# 会不会输出bar这个字符串,如果没有,如何修改才能打印出来.



#＃　看看主线程合适结束daemon线程
import time,threading

def foo(n):
    for i in range(n):
        print(i)
        time.sleep(1)
    
t1 = threading.Thread(target=foo,args=(10,),daemon=True) # 调换10和20看看效果.
t1.start()

t2 = threading.Thread(target=foo,daemon=False)
t2.start()

time.sleep(2)
print('Main Thread Exiting')
# 上例说明,如果有non-daemon线程时,主线程退出时,也不会杀掉所有daemon线程,直到所有non-daemon线程全部结束,如果还有daemon线程,主线程需要退出,会结束所有daemon线程,退出.
"""


# join方法的使用
"""
import threading,time,logging

logging.basicConfig(level=logging.INFO)

def worker():
    for x in range(10):
        msg = "{} is running".format(threading.current_thread())
        logging.info(msg)
        threading.Thread(target=worker1,name="worker1-{}".format(x),daemon=True).start()

def worker1():
    for x in range(10):
        msg = "$$$ {} is running".format(threading.current_thread())
        logging.info(msg)

t = threading.Thread(target=worker,daemon=True,name="worker-{}".format(0))
t.start()
t.join()

"""
"""
import time,threading

def foo(n):
    for i in range(n):
        print(i)
        time.sleep(1) 
    
t1 = threading.Thread(target=foo,args=(10,),daemon=True)
t1.start()
t1.join()  # 设置join 取消join对比一下．
print('Main Thread Exiting')
'''
使用了join方法后,daemon线程执行完了,主线程才退出了.

join(timeout=None),是线程的标准方法之一.
一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用线程终止.一个线程可以被join多次.
timeout参数指定调用者等待多久,没有设置超时,就一直等到被调用线程结束.
调用谁的join方法,就是join谁,就要等谁.
'''
"""

import time, threading
'''
def bar():
    while True:
        time.sleep(1)
        print('bar')

def foo():
    print("t1's daemon={}".format(threading.current_thread().isDaemon()))
    t2 = threading.Thread(target=bar)
    t2.start()
    print("t2's daemon = {}".format(t2.isDaemon()))

t1 = threading.Thread(target=foo,daemon=True)
t1.start()

time.sleep(3)
print('Main Thread Exiting')


def bar():
    while True:
        time.sleep(1)
        print('bar')

def foo():
    print("t1's daemon={}".format(threading.current_thread().isDaemon()))
    t2 = threading.Thread(target=bar)
    t2.start()
    print("t2's daemon={}".format(t2.isDaemon()))
    t2.join()

t1 = threading.Thread(target=foo,daemon=True)
t1.start()

t1.join()
time.sleep(3)
print('Main Thread Exiting')
'''



# threading.local类
import time,threading

'''
# 局部变量实现
def worker():
    x = 0
    for i in range(100):
        time.sleep(0.0001)
        x += 1
    print(threading.current_thread(),x)

for i in range(10):
    threading.Thread(target=worker).start()    
'''
'''
a = threading.local()

def worker():
    a.x = 0
    for i in range(100):
        time.sleep(0.0001)
        a.x += 1
    print(threading.current_thread(),a.x)
    print(a.__dict__)

for i in range(10):
    threading.Thread(target=worker).start()
# 上例使用多线程,每个线程完成不同的计算任务,x是局部变量.
# 能够改造成使用全局变量完成.
'''



# 使用全局变量完成 
class A:
    def __init__(self):
        self.x = 0
    
# 全局对象
global_data = A()

def worker():
    global_data.x = 0
    for i in range(100):
        time.sleep(0.001)
        global_data.x += 1
    print(threading.current_thread(),global_data.x)

for i in range(10):
    threading.Thread(target=worker).start()
# 上例虽然使用了全局对象,但是线程之间互相干扰,导致了错误的结果.能不能使用全局对象,还能保持每个线程使用不同的数据呢?




'''
python提供threading.local类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据其他线程看不见.
'''
import time,threading

'''
# 全局对象
ｇlobal_data = threading.local()

def worker():
    global_data.x = 0
    for i in range(100):
        time.sleep(0.001)
        global_data.x += 1
    print(threading.current_thread(),global_data.x)

for i in range(10):
    threading.Thread(target=worker).start()
# 结果显示和使用局部变量的效果一样.
'''

# threading.local的例子如下:
X = 'abc'
ctx = threading.local() #注意这个对象所处的线程
ｃtx.x = 123

print(ctx,type(ctx),ctx.x)

def worker():
    print(X)
    print(ctx)
    ctx.x = 56
    ctx.y = 222
    print(ctx.x)
    print(ctx.__dict__)
    print('working')

# worker() # 普通函数调用
# print()

threading.Thread(target=worker).start() # 另起一个线程
'''
从运行结果来看,另起一个线程打印ctx.x出错了.
AttributeError: '_thread._local' object has no attribute 'x'
但是,ctx打印没有出错,说明看到ctx,但是ctx中的x看不到,这个x不能跨线程.
'''







